﻿/**
 * Copyright (C) 2012-2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject;
using NGuice.Inject.Spi;
using System.Collections;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Util
{
    //完成!
    /// <summary>
    /// 创建且工作在Module实例上的静态方法
    /// </summary>
    public sealed class Modules
    {
        public static readonly IModule EMPTY_MODULE = new EmptyModule();

        /// <summary>
        /// 返回创建模块的构建器，此构建器将使用构造的模块覆盖给定的模块。如果key为模块集的边界，仅仅
        /// 覆盖的模块的绑定被保持。如果一个单独的PrivateModule被提供或所有元素都从一个单独的PrivateBinder
        /// ，此时将覆盖私有绑定。否则，私有绑定将不被覆盖除非被暴露。这可以被用于覆盖生产模块的绑定来替代
        /// 测试绑定：
        /// <code>
        /// Module functionalTestModule = Modules.Override(getProductionModules()).With(GetTestModules());
        /// </code>
        /// 执行写入更小的模块将能够被重复使用和测试而无需覆盖
        /// </summary>
        /// <param name="modules"></param>
        /// <returns></returns>
        public static OverriddenModuleBuilder Override(params IModule[] modules)
        {
            return new RealOverriddenModuleBuilder(modules.ToList());
        }

        /// <summary>
        /// 返回创建模块的构建器，此构建器将使用构造的模块覆盖给定的模块。如果key为模块集的边界，仅仅
        /// 覆盖的模块的绑定被保持。如果一个单独的PrivateModule被提供或所有元素都从一个单独的PrivateBinder
        /// ，此时将覆盖私有绑定。否则，私有绑定将不被覆盖除非被暴露。这可以被用于覆盖生产模块的绑定来替代
        /// 测试绑定：
        /// <code>
        /// Module functionalTestModule = Modules.Override(getProductionModules()).With(GetTestModules());
        /// </code>
        /// 执行写入更小的模块将能够被重复使用和测试而无需覆盖
        /// </summary>
        /// <param name="modules"></param>
        /// <returns></returns>
        public static OverriddenModuleBuilder Override(IEnumerable<IModule> modules)
        {
            return new RealOverriddenModuleBuilder(modules);
        }

        /// <summary>
        /// 返回一个新的安装到所有modules中的module
        /// </summary>
        /// <param name="modules"></param>
        /// <returns></returns>
        public static IModule Combine(params IModule[] modules)
        {
            return Combine(ImmutableSet<IModule>.Of(modules));
        }

        /// <summary>
        /// 返回一个新的安装到所有modules中的module
        /// </summary>
        /// <param name="modules"></param>
        /// <returns></returns>
        public static IModule Combine(IEnumerable<IModule> modules)
        {
            ISet<IModule> modulesSet = ImmutableSet<IModule>.CopyOf<IModule, IModule>(modules);
            return new InstallModule(modulesSet);
        }

        private class InstallModule : IModule
        {
            private ISet<IModule> modulesSet;

            public InstallModule(ISet<IModule> modulesSet)
            {
                this.modulesSet = modulesSet;
            }

            public void Configure(IBinder binder)
            {
                binder = binder.SkipSources(this.GetType());
                foreach (IModule module in modulesSet)
                {
                    binder.Install(module);
                }
            }
        }

        public interface OverriddenModuleBuilder
        {
            IModule With(params IModule[] overrides);

            IModule With(IEnumerable<IModule> overrides);

        }

        private sealed class RealOverriddenModuleBuilder : OverriddenModuleBuilder
        {
            private readonly ImmutableSet<IModule> baseModules;

            internal RealOverriddenModuleBuilder(IEnumerable<IModule> baseModules)
            {
                this.baseModules = ImmutableSet<IModule>.CopyOf<IModule, IModule>(baseModules);
            }

            public IModule With(params IModule[] overrides)
            {
                return With(overrides.ToList());
            }

            public IModule With(IEnumerable<IModule> overrides)
            {
                return new RealOverriddenModule(this, overrides);
            }

            private class RealOverriddenModule : AbstractModule
            {
                private RealOverriddenModuleBuilder builder;

                private IEnumerable<IModule> overrides;

                public RealOverriddenModule(RealOverriddenModuleBuilder builder, IEnumerable<IModule> overrides)
                {
                    this.builder = builder;
                    this.overrides = overrides;
                }

                protected override void Configure()
                {
                    IBinder baseBinder = Binder;
                    IList<IElement> baseElements = Elements.GetElements(builder.baseModules);

                    // If the sole element was a PrivateElements, we want to override
                    // the private elements within that -- so refocus our elements
                    // and binder.
                    if (baseElements.Count == 1)
                    {
                        IElement element = baseElements[0];
                        if (element is IPrivateElements)
                        {
                            IPrivateElements privateElements = (IPrivateElements)element;
                            IPrivateBinder privateBinder = baseBinder.NewPrivateBinder().WithSource(privateElements.Source);
                            foreach (Key exposed in privateElements.GetExposedKeys())
                            {
                                privateBinder.WithSource(privateElements.GetExposedSource(exposed)).Expose(exposed);
                            }
                            baseBinder = privateBinder;
                            baseElements = privateElements.Elements;
                        }
                    }

                    IBinder binder = baseBinder;
                    HashSet<IElement> elements = new HashSet<IElement>(baseElements);
                    IList<IElement> overrideElements = Elements.GetElements(overrides);

                    ISet<Key> overriddenKeys = new HashSet<Key>();
                    ISet<Type> overridesScopeAnnotations = new HashSet<Type>();

                    // execute the overrides module, keeping track of which keys and scopes are bound
                    new ModuleWriter1(binder, overriddenKeys, overridesScopeAnnotations).WriteAll(overrideElements);

                    Hashtable scopeInstancesInUse = new Hashtable();
                    List<ScopeBinding> scopeBindings = Lists.NewArrayList<ScopeBinding>();

                    // execute the original module, skipping all scopes and overridden keys. We only skip each
                    // overridden binding once so things still blow up if the module binds the same thing
                    // multiple times.
                    new ModuleWriter2(this, overridesScopeAnnotations, binder, overriddenKeys, scopeInstancesInUse, scopeBindings).WriteAll(elements);

                    // execute the scope bindings, skipping scopes that have been overridden. Any scope that
                    // is overridden and in active use will prompt an error
                    new ModuleWriter3(binder, overridesScopeAnnotations, scopeInstancesInUse).WriteAll(scopeBindings);
                }

                private class RealOverriddenDefaultBindingScopingVisitor : DefaultBindingScopingVisitor<IScope>
                {
                    public override IScope VisitScope(IScope scope)
                    {
                        return scope;
                    }
                }

                internal IScope GetScopeInstanceOrNull(IBinding binding)
                {
                    dynamic dynaBinding = binding;
                    //通过动态对象调用
                    return dynaBinding.AcceptScopingVisitor<IScope>(new RealOverriddenDefaultBindingScopingVisitor());
                }
            }

            private class ModuleWriter : DefaultElementVisitor<AsyncVoid>
            {
                protected readonly IBinder binder;

                internal ModuleWriter(IBinder binder)
                {
                    this.binder = binder;
                }

                protected override AsyncVoid VisitOther(IElement element)
                {
                    element.ApplyTo(binder);
                    return null;
                }

                internal void WriteAll(IEnumerable<IElement> elements)
                {
                    foreach (IElement element in elements)
                    {
                        element.AcceptVisitor(this);
                    }
                }
            }

            private class ModuleWriter1 : ModuleWriter
            {
                private ISet<Key> overriddenKeys;

                private ISet<Type> overridesScopeAnnotations;

                public ModuleWriter1(IBinder binder, ISet<Key> overriddenKeys, ISet<Type> overridesScopeAnnotations)
                    : base(binder)
                {
                    this.overriddenKeys = overriddenKeys;
                    this.overridesScopeAnnotations = overridesScopeAnnotations;
                }

                public override AsyncVoid Visit<T>(IBinding<T> binding)
                {
                    overriddenKeys.Add(binding.Key);
                    return base.Visit(binding);
                }

                public override AsyncVoid Visit(ScopeBinding scopeBinding)
                {
                    overridesScopeAnnotations.Add(scopeBinding.AnnotationType);
                    return base.Visit(scopeBinding);
                }

                public override AsyncVoid Visit(IPrivateElements privateElements)
                {
                    foreach (Key k in privateElements.GetExposedKeys())
                    {
                        overriddenKeys.Add(k);
                    }
                    return base.Visit(privateElements);
                }
            }

            private class ModuleWriter2 : ModuleWriter
            {
                private Hashtable scopeInstancesInUse;//key为IScope， value为object

                private List<ScopeBinding> scopeBindings;

                private ISet<Key> overriddenKeys;

                private RealOverriddenModule outer;

                private ISet<Type> overridesScopeAnnotations;

                public ModuleWriter2(RealOverriddenModule outer, ISet<Type> overridesScopeAnnotations, IBinder binder, ISet<Key> overriddenKeys, Hashtable scopeInstancesInUse, List<ScopeBinding> scopeBindings)
                    : base(binder)
                {
                    this.scopeInstancesInUse = scopeInstancesInUse;
                    this.scopeBindings = scopeBindings;
                    this.overriddenKeys = overriddenKeys;
                    this.outer = outer;
                    this.overridesScopeAnnotations = overridesScopeAnnotations;
                }

                public override AsyncVoid Visit<T>(IBinding<T> binding)
                {
                    if (!overriddenKeys.Remove(binding.Key))
                    {
                        base.Visit(binding);

                        // Record when a scope instance is used in a binding
                        IScope scope = outer.GetScopeInstanceOrNull(binding);
                        if (scope != null)
                        {
                            scopeInstancesInUse.Add(scope, binding.Source);
                        }
                    }
                    return null;
                }

                void Rewrite(IBinder binder, IPrivateElements privateElements, ISet<Key> keysToSkip)
                {
                    IPrivateBinder privateBinder = binder.WithSource(privateElements.Source).NewPrivateBinder();

                    ISet<Key> skippedExposes = new HashSet<Key>();

                    foreach (Key key in privateElements.GetExposedKeys())
                    {
                        if (keysToSkip.Remove(key))
                        {
                            skippedExposes.Add(key);
                        }
                        else
                        {
                            privateBinder.WithSource(privateElements.GetExposedSource(key)).Expose(key);
                        }
                    }

                    foreach (IElement element in privateElements.Elements)
                    {
                        if (element is IBinding && skippedExposes.Remove(((IBinding)element).Key))
                        {
                            continue;
                        }
                        if (element is IPrivateElements)
                        {
                            Rewrite(privateBinder, (IPrivateElements)element, skippedExposes);
                            continue;
                        }
                        element.ApplyTo(privateBinder);
                    }
                }

                public override AsyncVoid Visit(IPrivateElements privateElements)
                {
                    Rewrite(binder, privateElements, overriddenKeys);
                    return null;
                }

                public override AsyncVoid Visit(ScopeBinding scopeBinding)
                {
                    scopeBindings.Add(scopeBinding);
                    return null;
                }
            }

            private class ModuleWriter3 : ModuleWriter
            {
                private Hashtable scopeInstancesInUse;//key为IScope， value为object

                private ISet<Type> overridesScopeAnnotations;

                public ModuleWriter3(IBinder binder, ISet<Type> overridesScopeAnnotations, Hashtable scopeInstancesInUse)
                    : base(binder)
                {
                    this.scopeInstancesInUse = scopeInstancesInUse;
                    this.overridesScopeAnnotations = overridesScopeAnnotations;
                }

                public override AsyncVoid Visit(ScopeBinding scopeBinding)
                {
                    if (!overridesScopeAnnotations.Remove(scopeBinding.AnnotationType))
                    {
                        base.Visit(scopeBinding);
                    }
                    else
                    {
                        object source = scopeInstancesInUse[scopeBinding.Scope];
                        if (source != null)
                        {
                            binder.WithSource(source).AddError(
                                "The scope for @%s is bound directly and cannot be overridden.",
                                scopeBinding.AnnotationType.Name);
                        }
                    }
                    return null;
                }
            }
        }

        private class EmptyModule : IModule
        {
            public void Configure(IBinder binder) { }
        }
    }
}
